#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/foreach.hpp>
#include "game_logic.h"
//#include "chipmunk.h"
#include "Td_object.h"
#include "totem_obj_config.h"
#include <fstream>
#include "animation.h"
#include "utils.h"
#include <boost/scoped_ptr.hpp>

extern void loadGameMusic();
using namespace std;
using namespace boost;

template <typename T>
struct singleton_default
{
public:
    typedef T object_type;
	static scoped_ptr<T> obj;

    inline static object_type & instance()
    {
		if (0 == obj.get()) obj.reset(new object_type());
		return *(obj.get());
    }
};
template <typename T> scoped_ptr<T> singleton_default<T>::obj;

#ifndef FREE_VERSION
const char * const LOG_FILE_NAME = "game_log";
#else
const char * const LOG_FILE_NAME = "game_free_log";
#endif

struct t_game_log_info {
	int total_level;
	int cur_level;
	int available_level;
	int total_score;
	bool is_play_music;
	vector<int> level_scores;
	
	template<class Archive>	
	void serialize(Archive & ar, const unsigned int version)
	{	
		ar  & BOOST_SERIALIZATION_NVP(total_level);		
		ar  & BOOST_SERIALIZATION_NVP(available_level);
		ar  & BOOST_SERIALIZATION_NVP(cur_level);
		ar  & BOOST_SERIALIZATION_NVP(is_play_music);
		ar  & BOOST_SERIALIZATION_NVP(level_scores);
	}
	t_game_log_info() {
		//getNsAutoreleasePoolInstance();
		
		std::string filename = getResPath(LOG_FILE_NAME);
		if (0 != strcmp(filename.c_str(), "")) {
			std::ifstream fs(filename.c_str());
			boost::archive::binary_iarchive ar(fs);
			ar & boost::serialization::make_nvp("game_log_info", *this);
		}
		else {
#ifndef FREE_VERSION
			total_level = 200;
#else
			total_level = 30;
#endif
			available_level = 1;
			cur_level = 0;
			is_play_music = true;
			level_scores.reserve(total_level);
			for (int i = 0; i < total_level; i++) level_scores.push_back(0);
		}
	}
	
	void log2file() {
		string filename(getWritableFilePath(LOG_FILE_NAME));
		std::ofstream fs(filename.c_str());
		boost::archive::binary_oarchive ar(fs);
		ar & boost::serialization::make_nvp("game_log_info", *this);
	}
	~t_game_log_info() {
		//log2file();
	}
};

BOOST_CLASS_VERSION(t_game_log_info, 1)

extern void CreateObjs(totem_destroyer::SpaceMgr * spaceManager, int level);
boost::scoped_ptr<totem_destroyer::SpaceMgr> g_spaceMgr;
std::vector<TotemObject_t*> g_TotemObjects;
map_info_t curr_level_info;
boost::shared_ptr<texture_t> g_backGroundImg;
SceneAnimation_delegate_t g_sceneAnimation;


namespace read_config{	
	void init();
};
namespace game_logic {

	t_game_status g_gameStatus;
	
	void init()
	{
		read_config::init();
	}
	
	void deinit()
	{
	}
	
	void removeShapeBody(cpShape *shape, void *data)
	{
		g_spaceMgr->remove_shape(shape);		
	}
	void mouseDown(point2d_t point)
	{
		//std::cout << point << std::endl;
		if (0 >= remainSteps()) return ;
		cpSpacePointQuery(g_spaceMgr->m_space, cpv(point[0], point[1]), CP_ALL_LAYERS, CP_NO_GROUP, removeShapeBody, NULL); 
	}
	
	int CurLevel() {
		ASSERT(singleton_default<t_game_log_info>::instance().cur_level <= singleton_default<t_game_log_info>::instance().available_level);
		ASSERT(singleton_default<t_game_log_info>::instance().cur_level >= 0);
		return singleton_default<t_game_log_info>::instance().cur_level;
	}
	void setCurLevel(int level)
	{
		ASSERT(level >= 0 && level <= singleton_default<t_game_log_info>::instance().available_level);
		singleton_default<t_game_log_info>::instance().cur_level = level;
	}
	int &totalLevel()
	{
		return singleton_default<t_game_log_info>::instance().total_level;
	}
	int &score(int level)
	{
		return singleton_default<t_game_log_info>::instance().level_scores[level];
	}
	
	int maxAvailableLevel() {
		return singleton_default<t_game_log_info>::instance().available_level;
	}
	bool &isMusicPlay() {
		return singleton_default<t_game_log_info>::instance().is_play_music;
	}
	
	void nextLevel() {
		const int TO_NEXT_NUM = 2;
		t_game_log_info & log_info = singleton_default<t_game_log_info>::instance();
		
		if (log_info.available_level < (log_info.cur_level+TO_NEXT_NUM) ){
			log_info.available_level = log_info.cur_level+TO_NEXT_NUM;
		}		
		if (log_info.available_level >= log_info.total_level) {
			log_info.available_level = log_info.total_level-1;
		}
		log_info.cur_level++;
		if (log_info.cur_level > log_info.available_level) {
			log_info.cur_level = log_info.available_level;
		}
		log_info.log2file();
	}
//	bool allLevelsPass() {
//		t_game_log_info & log_info = singleton_default<t_game_log_info>::instance();
//	}
	int &remainSteps() {
		return curr_level_info.passSteps;
	}
	bool isFinally()
	{
		t_game_log_info & log_info = singleton_default<t_game_log_info>::instance();
		return ( (log_info.cur_level == log_info.available_level) && (log_info.cur_level == (log_info.total_level-1)) );
	}
	
	inline bool isFallDown() {
		bool ret = false;
		bool game_pass = (kGameCheck == g_gameStatus);
		if (kGameOver == g_gameStatus || kGamePass == g_gameStatus || kGameWin == g_gameStatus) return ret;
		const float FALL_DOWN_MAX = SCENE_EDGES[0].y + kSegmentShapeThickness + 3.6E-002;
		const float ZERO_SPEED = 3.6E-001;
		BOOST_FOREACH(TotemObject_t* obj, g_TotemObjects){
			cpPolyShape *poly = (cpPolyShape *)obj->m_pShape;			
			point2d_t *vts = obj->GetVerts();
			if (obj->m_pBoby->p.y < 0) {
				obj->setObjStatus(kTotemObjOver);
				ret = true;
				g_gameStatus = kGameOver;
			}
			else {
				for(int i=0; i<poly->numVerts; i++) {
					if(FALL_DOWN_MAX >= vts[i][1]) {
						obj->setObjStatus(kTotemObjOver);
						ret = true;
						g_gameStatus = kGameOver;
						//todo add music
						//playBroken();
					}
				}
			}
			if (game_pass) game_pass &= (cpvlength(obj->m_pBoby->v) < ZERO_SPEED);			
		}
		if (game_pass && (!ret) ) {
			ASSERT(kGameCheck == g_gameStatus);
			g_gameStatus = kGamePass;
			BOOST_FOREACH(TotemObject_t* obj, g_TotemObjects){
				obj->setObjStatus(kTotemObjPass);
			}
			score(CurLevel()) = 1;
			if (isFinally()) g_gameStatus = kGameWin;
			else nextLevel();
		}
		return ret;
	}

	const std::string &getCurLevelBgImag()
	{
		return curr_level_info.bgImg;
	}
	void gameStart()
	{
		g_TotemObjects.clear();
		g_spaceMgr.reset(new totem_destroyer::SpaceMgr);
		CreateObjs(g_spaceMgr.get(), CurLevel());
		curr_level_info = maps::getMapinfo(CurLevel());
		g_gameStatus = kGameNormal;
		g_backGroundImg.reset(new texture_t(curr_level_info.bgImg.c_str()) );
		boost::function<void()> fn = NULL;
		if (NULL == fn) {
		}
		//todo add music
		//loadGameMusic();
		setSceneAnimation(g_sceneAnimation, curr_level_info.bgImg);
	}
	
	void outOfStage()
	{
		vector<cpShape*> has2remove;
		BOOST_FOREACH(cpShape* tmp, g_spaceMgr->m_resFree){
			cpBody *body = tmp->body;
			action_t *p = (action_t *)tmp->data;
			const totem_obj_info_t *obj_info = (const totem_obj_info_t *)p->_getObjInfo();
			if (body->p.y < 0){
				if(boost::tuples::get<0>(p->is_explosive())) {
					remainSteps()--;
					//ASSERT(0 <= remainSteps());
					p->_remove_from_space(g_spaceMgr->m_space);
					p->_free();
					has2remove.push_back(tmp);			
				}

			}
			else if (NULL != obj_info && body->p.y > SCREEN_HEIGHT) {
				if( boost::tuples::get<0>(p->is_explosive()) &&  string::npos != obj_info->texture_name.find(STR_LIGHT_BOX) ) {
					remainSteps()--;
					//ASSERT(0 <= remainSteps());
					p->_remove_from_space(g_spaceMgr->m_space);
					p->_free();
					has2remove.push_back(tmp);			
				}
			}
		}
		
		BOOST_FOREACH(cpShape* shape, has2remove) {
			g_spaceMgr->m_resFree.erase(shape);
		}
		
	}
	
	t_game_status checkGameSatus() {
		isFallDown();
		if (kGameNormal == g_gameStatus) {
			if (0 >= remainSteps()) g_gameStatus = kGameCheck;
		}
		else if (kGameCheck == g_gameStatus) {
			
		}
		
		return g_gameStatus;
	}

	t_game_status step(float_t dt)
	{
		const int step = 4;
		const float_t _dt = kINTERVAL / step;
		for (int i = 0; i < step; i++) g_spaceMgr->update(_dt);
		outOfStage();
		return checkGameSatus();
	}

	void display()
	{
		//glClear(GL_COLOR_BUFFER_BIT);
		glColor4f(1.0, 1.0, 1.0, 1.0);
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		g_backGroundImg->Draw();
		if (NULL != g_sceneAnimation._update) g_sceneAnimation._update();
		g_spaceMgr->display();		
	}
	
	void addTotemObject(TotemObject_t* obj)
	{
		g_TotemObjects.push_back(obj);
	}
};
